package gptma.bo.dao.task;

import gptma.bo.dao.GenericDaoJpaImpl;
import gptma.bo.model.employee.Employee;
import gptma.bo.model.project.Project;
import gptma.bo.model.task.Task;
import gptma.bo.model.task.TaskHistoryElement;
import gptma.bo.search.TaskSearchCriteria;
import gptma.bo.util.GptmaUtils;

import java.util.Date;
import java.util.List;

import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;

public class TaskDaoImpl extends GenericDaoJpaImpl<Task, Long> implements
		TaskDao {

	@Override
	@SuppressWarnings("unchecked")
	public List<Task> findAll(boolean admin) {
		return em.createNamedQuery("Task.findAll").setParameter("admin", admin)
				.getResultList();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Task> findByCriteria(TaskSearchCriteria criteria) {
		StringBuilder query = new StringBuilder(
				"select t from Task t where 1=1 ");

		// code
		String code = criteria.getCode();
		boolean codeSet = false;
		if (!StringUtils.isEmpty(code)) {
			query.append("and t.code = :code ");
			codeSet = true;
		}

		// label
		String label = criteria.getLabel();
		boolean labelSet = false;
		if (!StringUtils.isEmpty(label)) {
			query.append("and t.label like :label ");
			labelSet = true;
		}

		// reference
		String ref = criteria.getClientRequestReference();
		boolean refSet = false;
		if (!StringUtils.isEmpty(ref)) {
			query.append("and t.clientRequest.clientReference = :ref ");
			refSet = true;
		}

		// administrative
		Boolean admin = criteria.getAdministrative();
		boolean adminSet = false;
		if (admin != null) {
			query.append("and t.administrative = :admin ");
			adminSet = true;
		}

		// assigned employees (ored: PAE or FRR)
		List<Employee> assigned = criteria.getEmployees();
		boolean assignedSet = false;
		if (assigned != null && !assigned.isEmpty()) {
			query.append("and exists (select a from Assignment a where a.task = t and a.employee in (:assigned)) ");
			assignedSet = true;
		}

		// projects (directly attached or through a client request) ored
		List<Project> projects = criteria.getProjects();
		boolean projectsSet = false;
		if (projects != null && !projects.isEmpty()) {
			query.append("and (t.directlyAttachedProject in (:projects) ")
					.append("or t.clientRequest.project in (:projects)) ");
			projectsSet = true;
		}

		Query q = em.createQuery(query.toString());
		if (codeSet) {
			q.setParameter("code", code);
		}
		if (labelSet) {
			q.setParameter("label", generateContainsCriteria(label));
		}
		if (refSet) {
			q.setParameter("ref", ref);
		}
		if (adminSet) {
			q.setParameter("admin", admin);
		}
		if (assignedSet) {
			q.setParameter("assigned", assigned);
		}
		if (projectsSet) {
			q.setParameter("projects", projects);
		}

		List<Task> ret = q.getResultList();

		return ret;
	}

	@Override
	public TaskHistoryElement findCurrentState(Task task) {
		return (TaskHistoryElement) em
				.createNamedQuery("Task.findCurrentState")
				.setParameter("task", task).setFirstResult(0).getSingleResult();
	}

	@Override
	public boolean findExistingCode(String code) {
		Long count = (Long) em.createNamedQuery("Task.findExistingCode")
				.setParameter("code", code).getSingleResult();

		return (count > 0);
	}

	@Override
	public int findMaxPriority() {
		return ((Long) em.createNamedQuery("Task.findMaxPriority")
				.getSingleResult()).intValue();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Task> findNonAttached() {
		return em.createNamedQuery("Task.findNonAttached").getResultList();
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Task> findNonAssigned() {
		return em.createNamedQuery("Task.findNonAssigned").getResultList();
	}

	@Override
	public float findConsolidatedCharge(Task task, Date date) {
		// reset time info so that query can be cached
		Date queryDate = GptmaUtils.resetTimeInfo(date);
		return ((Double) em.createNamedQuery("Task.findConsolidatedCharge")
				.setParameter("task", task).setParameter("date", queryDate)
				.setHint(HIBERNATE_QUERY_CACHE, true).getSingleResult())
				.floatValue();
	}

}
